package com.xinqi.modules.course.clazz.service.impl;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.xinqi.common.base.enums.QrCodeEnum;
import com.xinqi.common.base.enums.YesNoEnum;
import com.xinqi.common.base.error.CommonErrors;
import com.xinqi.common.base.response.PageInfo;
import com.xinqi.common.base.response.Param;
import com.xinqi.common.base.response.R;
import com.xinqi.common.base.user.Account;
import com.xinqi.common.base.utils.json.JSON;
import com.xinqi.common.core.constant.CacheConstants;
import com.xinqi.common.core.constant.ConfigKeyConstants;
import com.xinqi.common.core.enums.ClassRoleEnum;
import com.xinqi.common.core.utils.MessageUtils;
import com.xinqi.common.core.utils.StringUtils;
import com.xinqi.common.mybatis.data.service.impl.BaseServiceImpl;
import com.xinqi.common.mybatis.utils.Pages;
import com.xinqi.common.mybatis.utils.Queries;
import com.xinqi.common.redis.utils.RedisUtils;
import com.xinqi.common.satoken.utils.LoginHelper;
import com.xinqi.modules.chat.chat.group.client.RemoteChatClient;
import com.xinqi.modules.chat.chat.group.dto.req.ChatClassUserGroupCreateDTO;
import com.xinqi.modules.course.classmenu.service.ClassMenuService;
import com.xinqi.modules.course.clazz.convert.ClassConvert;
import com.xinqi.modules.course.clazz.convert.ClassGroupStudentConvert;
import com.xinqi.modules.course.clazz.convert.ClassJoinAuditConvert;
import com.xinqi.modules.course.clazz.convert.ClassStudentConvert;
import com.xinqi.modules.course.clazz.domain.ClassEntity;
import com.xinqi.modules.course.clazz.domain.ClassGroupEntity;
import com.xinqi.modules.course.clazz.domain.ClassGroupStudentEntity;
import com.xinqi.modules.course.clazz.domain.ClassStudentEntity;
import com.xinqi.modules.course.clazz.dto.req.*;
import com.xinqi.modules.course.clazz.dto.rsp.*;
import com.xinqi.modules.course.clazz.mapper.ClassMapper;
import com.xinqi.modules.course.clazz.service.*;
import com.xinqi.modules.course.clazz.thread.ClassMessageSyncService;
import com.xinqi.modules.course.clazz.thread.dto.ClassStudentRemindDTO;
import com.xinqi.modules.course.content.service.CourseContentService;
import com.xinqi.modules.course.course.constant.CourseConstants;
import com.xinqi.modules.course.course.dto.rsp.CourseResultDTO;
import com.xinqi.modules.course.course.enums.ClassAuditEnum;
import com.xinqi.modules.course.course.enums.ClassStatusEnum;
import com.xinqi.modules.course.course.enums.clazz.ClassAuditStatusEnum;
import com.xinqi.modules.course.course.enums.clazz.ClassOperationLogTypeEnum;
import com.xinqi.modules.course.course.service.CourseService;
import com.xinqi.modules.stats.assistant.dto.req.RemindCheckDTO;
import com.xinqi.modules.sys.SystemApiConfig;
import com.xinqi.modules.sys.config.client.SysConfigClient;
import com.xinqi.modules.user.user.client.UserClient;
import com.xinqi.modules.user.user.dto.rsp.UserResultDTO;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @version V1.0
 * @Title:
 * @ClassName: ClassServiceImpl.java
 * @Description: 班级业务类
 * @Copyright 2022-2023 www.xinqi.com - Powered By 星奇科技
 * @author: wenjie
 * @date: 2023/3/11 14:36
 */
@Service("classService")
@RequiredArgsConstructor
public class ClassServiceImpl extends BaseServiceImpl<ClassMapper, ClassEntity> implements ClassService {
    private final ClassConvert classConvert;
    private final ClassMapper classMapper;
    @Lazy
    private final ClassGroupService classGroupService;
    @Lazy
    private final ClassStudentService classStudentService;
    @Lazy
    private final ClassGroupStudentService classGroupStudentService;
    @Lazy
    private final ClassGroupStudentConvert classGroupStudentConvert;
    @Lazy
    private final ClassJoinAuditService classJoinAuditService;
    @Lazy
    private final ClassOperationLogService classOperationLogService;
    private final ClassMenuService classMenuService;
    @Lazy
    private final CourseService courseService;
    private final CourseContentService courseContentService;

    private final ClassStudentConvert classStudentConvert;
    private final ClassJoinAuditConvert classJoinAuditConvert;

    private final ClassCourseCodeService classCourseCodeService;

    private final UserClient userClient;

    private final SysConfigClient sysConfigClient;

    @Lazy
    private final ClassMessageSyncService classMessageSyncService;
    private final ClassMqService classMqService;
    private final RemoteChatClient remoteChatClient;

    /**
     * 分页查询 班级表数据
     */
    @Override
    @DS("slaver")
    public PageInfo<ClassResultDTO> page(Param pageable, ClassQueryDTO query) {
        IPage<ClassEntity> page = super.page(Pages.page(pageable), wrapper(query));
        return Pages.convert(page, classConvert::convert);
    }

    /**
     * 查询 班级表数据
     */
    @Override
    public List<ClassResultDTO> find(ClassQueryDTO query) {
        return list(wrapper(query)).stream().map(classConvert::convert).collect(Collectors.toList());
    }

    private LambdaQueryWrapper<ClassEntity> wrapper(ClassQueryDTO query) {
        LambdaQueryWrapper<ClassEntity> wrapper = Queries.lambda();
        Queries.accept(query.getId(), v -> wrapper.eq(ClassEntity::getId, v));
        Queries.accept(query.getCourseId(), v -> wrapper.eq(ClassEntity::getCourseId, v));
        Queries.accept(query.getUserId(), v -> wrapper.eq(ClassEntity::getUserId, v));
        Queries.accept(query.getCourseCode(), v -> wrapper.eq(ClassEntity::getCourseCode, v));
        return wrapper;
    }

    /**
     * 根据id查询 班级表数据
     */
    @Override
    public ClassResultDTO findById(Long id) {
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(id), "ID不能为空");
        return Optional.ofNullable(classMapper.selectById(id)).map(classConvert::convert).orElse(null);
    }

    /**
     * 新增 班级表数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long insert(ClassCreateDTO dto) {
        CommonErrors.BAD_REQUEST.check(dto.getCourseId() != null, "缺少课程ID");
        CommonErrors.BAD_REQUEST.check(dto.getAuditInfo().size() <= CourseConstants.AuditLabelCount.LABEL_COUNT, "一个班级最多添加七个审核标签");
        checkClassName(dto.getCourseId(), dto.getName(), null);
        ClassEntity entity = classConvert.create(dto);
        classCode(entity);
        // 设置 拥有者
        entity.setUserId(LoginHelper.getUserId());
        entity.setCreateBy(LoginHelper.getUserId());
        // 保存 课程
        super.save(entity);
        // 添加 老师
        classStudentService.insertTeacher(LoginHelper.getUserId(), entity.getCourseId(), entity.getId());

        //异步处理创建群聊，如果创建群聊失败，则不影响主流程。不建议在创建班级的时候调用创建群聊。耦合严重，调用链太长，影响性能。推荐的做法是：课程和班级解耦、聊天和课程解耦
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            @Override
            public void afterCommit() {
                // 同步课程菜单
                classMenuService.initClassMenu(entity.getId(), entity.getCourseId());
                // 同步群聊
                createChatGroup(entity);
            }
        });

        return entity.getId();
    }

    /**
     * 批量添加班级信息
     */
    @Override
    public List<Long> insertNew(List<ClassCreateDTO> classList) {
        CommonErrors.BAD_REQUEST.check(CollectionUtils.isNotEmpty(classList), "缺少班级信息");
        Long userId = LoginHelper.getUserId();
        List<ClassEntity> list = classList.stream().map(item -> {
            return checkClassNew(item, classList, userId);
        }).collect(Collectors.toList());
        this.saveBatch(list);

        // 批量添加老师
        List<ClassStudentEntity> studentList = list.stream().map(item -> {
            ClassStudentEntity entity = classStudentConvert.convert(item.getCourseId(), item.getId(), LoginHelper.getUserId());
            entity.setIsTeacher(YesNoEnum.YES.ordinal());
            entity.setIsTop(YesNoEnum.NO.ordinal());
            entity.setClassRole(ClassRoleEnum.TEACHER);
            entity.setCreateBy(LoginHelper.getUserId());
            entity.setStudentName(LoginHelper.getNickname());
            entity.setRelationship(ClassRoleEnum.TEACHER.getDescribe());
            return entity;
        }).collect(Collectors.toList());
        classStudentService.addBatch(studentList);

        // 批量新增菜单
        classMenuService.initClassMenu(list);

        //异步处理创建群聊，如果创建群聊失败，则不影响主流程。不建议在创建班级的时候调用创建群聊。耦合严重，调用链太长，影响性能。推荐的做法是：课程和班级解耦、聊天和课程解耦
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            @Override
            public void afterCommit() {
                list.forEach(entity -> {
                    // 同步群聊
                    createChatGroup(entity);
                });
            }
        });
        return list.stream().map(ClassEntity::getId).collect(Collectors.toList());
    }

    /**
     * 新增 班级 -- 更新
     */
    private ClassEntity checkClassNew(ClassCreateDTO dto, List<ClassCreateDTO> list, Long userId) {
        CommonErrors.BAD_REQUEST.check(dto.getCourseId() != null, "缺少课程ID");
        CommonErrors.BAD_REQUEST.check(dto.getAuditInfo().size() <= CourseConstants.AuditLabelCount.LABEL_COUNT, "一个班级最多添加七个审核标签");
        // 更新 判断传入班级List是否有重复的班级名称
        checkClassNameNew(list, dto.getName());
        ClassEntity entity = classConvert.create(dto);
        classCode(entity);
        // 设置 拥有者
        entity.setUserId(userId);
        entity.setCreateBy(userId);
        return entity;
    }

    private void createChatGroup(ClassEntity entity) {
        CourseResultDTO course = courseService.findByClassId(entity.getId());

        ChatClassUserGroupCreateDTO group = new ChatClassUserGroupCreateDTO();
        group.setCourseName(course.getCourseName());
        group.setClassName(entity.getName());
        group.setClassId(entity.getId());
        group.setUserId(entity.getUserId());
        //生成群聊
        remoteChatClient.classCreateGroup(group);
    }

    /**
     * 创建群聊(熟悉新奇)
     *
     * @author dzy
     */
    @Override
    public void createClassChatGroup(ClassEntity entity, List<Long> studentIds, Long userId) {
        ChatClassUserGroupCreateDTO group = new ChatClassUserGroupCreateDTO();
        group.setCourseName("熟悉新奇");
        group.setClassName(entity.getName());
        group.setClassId(entity.getId());
        group.setUserId(userId);
        group.setGroupUserIds(studentIds);
        //生成群聊
        remoteChatClient.classCreateGroup(group);
    }

    private void classCode(ClassEntity entity) {
        // 设置 加课码
        entity.setCourseCode(classCourseCodeService.getClassCode());
        Map<String, Object> mapData = new HashMap<>();
        mapData.put("type", QrCodeEnum.QR_ADD_CLASS.getCode());
        mapData.put("code", entity.getCourseCode());
        mapData.put("auditMethod", entity.getAuditMethod());
        String genQrCodeUrl = RedisUtils.getCacheMapValue(CacheConstants.SYS_CONFIG_HASH_KEY, ConfigKeyConstants.SYS_GEN_QRCODE_URL);
        String content = genQrCodeUrl + JSON.stringify(mapData);
        try {
            entity.setQrCode(content);
        } catch (Exception ignored) {
            throw CommonErrors.INTERNAL_SERVER_ERROR.asException("生成二维码错误");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<Long> insert(List<ClassCreateDTO> classList) {
        CommonErrors.BAD_REQUEST.check(CollectionUtils.isNotEmpty(classList), "缺少班级信息");
        // return classList.stream().map(this::insert).collect(Collectors.toList());
        return this.insertNew(classList);
    }

    /**
     * 根据id修改 班级表数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateById(ClassUpdateDTO dto) {
        if (Objects.nonNull(dto.getAuditInfo())) {
            CommonErrors.BAD_REQUEST.check(dto.getAuditInfo().size() <= CourseConstants.AuditLabelCount.LABEL_COUNT, "一个班级最多添加七个审核标签");
        }
        ClassEntity old = Optional.ofNullable(getById(dto.getId())).orElseThrow(() -> CommonErrors.BAD_REQUEST.asException("数据不存在"));

        // 班级名称修改 只能针对目前传入的List做校验
        // 跟原有数据做对比会导致 互换名称无法正常修改
        // checkClassName(dto.getCourseId(), dto.getName(), old.getId());
        //班级审核状态从手动审核改为自动审核时需要判断是否有待审核的学生需要审核
        if (Objects.equals(ClassAuditEnum.MANUAL, old.getAuditMethod()) && Objects.equals(ClassAuditEnum.AUTO, dto.getAuditMethod())) {
            long count = classJoinAuditService.findNotAuditCountByClassId(old.getId());
            CommonErrors.BAD_REQUEST.check(count <= 0, "当前存在未审核的成员，请先审核");
        }
        ClassEntity entity = classConvert.update(dto);
        return super.updateById(entity);
    }

    /**
     * 根据id删除 班级表数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteById(Long id) {
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(id), "ID不能为空");
        return removeById(id);
    }

    /**
     * 根据id删除 班级表数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteById(ClassDeleteDTO dto) {
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(dto.getId()), "ID不能为空");
        return deleteById(dto.getId());
    }

    private void checkClassName(Long courseId, String name, Long id) {
        if (isExistsByName(courseId, name, id)) {
            throw CommonErrors.BAD_REQUEST.asException("请勿添加重复班级");
        }
    }

    /**
     * 判断班级名称是否重复
     */
    @Override
    public Boolean isExistsByName(Long courseId, String name, Long id) {
        LambdaQueryWrapper<ClassEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ClassEntity::getCourseId, courseId);
        wrapper.eq(ClassEntity::getName, name);
        if (id != null) {
            wrapper.ne(ClassEntity::getId, id);
        }
        return count(wrapper) > 0;
    }

    /**
     * 获取班级信息跟传入的班级信息做对比即可
     *
     * @author dzy
     */
    private void checkClassNameNew(List<ClassCreateDTO> list, String name) {
        int count = 0;
        for (ClassCreateDTO clazz : list) {
            if (Objects.equals(clazz.getName(), name)) {
                count++;
                if (count > 1) {
                    throw CommonErrors.BAD_REQUEST.asException("请勿添加重复班级");
                }
            }
        }
    }

    @Override
    public List<ClassResultDTO> findByCourseId(Long courseId) {
        ClassQueryDTO query = new ClassQueryDTO();
        query.setCourseId(courseId);
        List<ClassResultDTO> list = find(query);

        if (CollectionUtils.isNotEmpty(list)) {
            // 获取加班连接地址
            String joinAddress = R.as(sysConfigClient.getConfigKey(ConfigKeyConstants.SYS_JOIN_CLASS_ADDRESS));
            // 获取用户信息
            UserResultDTO user = R.as(userClient.findById(list.get(0).getUserId()));
            list.forEach(item -> {
                item.setJoinAddress(joinAddress + "?" + "code=" + item.getCourseCode()
                    + "&nickName=" + user.getNickname() + "&avatar=" + user.getAvatar());
            });
        }

        return list;
    }

    /**
     * 查询课程下的我管理的班级（剔除当前班级） -- 老师、助教
     *
     * @param classId
     * @param studentId
     * @return
     */
    @Override
    public List<ClassResultDTO> findMyCreateClassByClassId(Long classId, Long studentId) {
        ClassEntity old = getById(classId);
        if (Objects.isNull(old)) {
            return new ArrayList<>();
        }
        List<ClassResultDTO> list = this.baseMapper.findByCourseIdAndRoles(old.getCourseId(), null, studentId, Arrays.asList(ClassRoleEnum.TEACHER.getCode(), ClassRoleEnum.ASSISTANT.getCode()), YesNoEnum.NO.ordinal());
        return list.stream().filter(item -> !Objects.equals(classId, item.getId())).collect(Collectors.toList());
    }

    /**
     * 根据id修改 班级表数据
     * 打开人数限制开关的时候需要判断目前班级人数，如果目前班级人数大于限制的阈值，修改不成功
     */
    @Override
    public Boolean openCountLimit(ClassCountLimitUpdateDTO dto) {
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(dto.getStudentCount()), "要限制的学生人数不能为空");
        ClassEntity old = Optional.ofNullable(getById(dto.getId())).orElseThrow(() -> CommonErrors.BAD_REQUEST.asException("班级数据不存在"));

        List<ClassStudentResultDTO> studentList = classStudentService.findClassMasterStudentByClassId(dto.getId());
        CommonErrors.BAD_REQUEST.check(CollectionUtils.isEmpty(studentList) || dto.getStudentCount() >= studentList.size(), "要限制的学生人数不能小于当前班级人数");

        old.setIsStudentCount(YesNoEnum.YES.ordinal());
        old.setStudentCount(dto.getStudentCount());
        return updateById(old);
    }

    @Override
    public Boolean closeCountLimit(Long classId) {
        ClassEntity old = Optional.ofNullable(getById(classId)).orElseThrow(() -> CommonErrors.BAD_REQUEST.asException("班级数据不存在"));
        old.setIsStudentCount(YesNoEnum.NO.ordinal());
        return updateById(old);
    }

    @Override
    public Boolean allowDropClass(Long id) {
        ClassEntity old = Optional.ofNullable(getById(id)).orElseThrow(() -> CommonErrors.BAD_REQUEST.asException("数据不存在"));
        old.setIsDropClass(YesNoEnum.YES.ordinal());
        return updateById(old);
    }

    @Override
    public Boolean forbidDropClass(Long id) {
        ClassEntity old = Optional.ofNullable(getById(id)).orElseThrow(() -> CommonErrors.BAD_REQUEST.asException("数据不存在"));
        old.setIsDropClass(YesNoEnum.NO.ordinal());
        return updateById(old);
    }

    /**
     * 级联更新班级，包含删除、更新、新增三种情况
     * 例如：oldIdList=[1,2,3]; classIdList=[2,3,4];则：删除1，增加4，更新2和3
     *
     * @param courseId  课程id
     * @param classList 班级列表
     * @return 更新成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean update(Account user, Long courseId, List<ClassUpdateDTO> classList) {
        CommonErrors.BAD_REQUEST.check(CollectionUtils.isNotEmpty(classList), "缺少班级列表");

        List<ClassResultDTO> oldList = findByCourseId(courseId);
        if (CollectionUtils.isNotEmpty(oldList)) {
            List<Long> classIdList = classList.stream().map(ClassUpdateDTO::getId).collect(Collectors.toList());
            List<Long> preDeletedIdList = oldList.stream().map(ClassResultDTO::getId).filter(id -> !classIdList.contains(id)).collect(Collectors.toList());
            this.batchDeleteClassAndSubInfo(user, courseId, preDeletedIdList);
        }

        classList.forEach(c -> {
            if (c.getId() != null) {
                updateById(c);
            } else {
                Long id = insert(classConvert.convert(c));
                c.setId(id);
            }
        });

        return true;
    }

    /**
     * 级联更新班级，包含删除、更新、新增三种情况
     * 例如：oldIdList=[1,2,3]; classIdList=[2,3,4];则：删除1，增加4，更新2和3
     *
     * @param courseId  课程id
     * @param classList 班级列表
     * @return 返回新增的ID集合
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<Long> updateNew(Account user, Long courseId, List<ClassUpdateDTO> classList) {

        CommonErrors.BAD_REQUEST.check(CollectionUtils.isNotEmpty(classList), "缺少班级列表");

        List<ClassResultDTO> oldList = findByCourseId(courseId);
        if (CollectionUtils.isNotEmpty(oldList)) {
            List<Long> classIdList = classList.stream().map(ClassUpdateDTO::getId).collect(Collectors.toList());
            List<Long> preDeletedIdList = oldList.stream().map(ClassResultDTO::getId).filter(id -> !classIdList.contains(id)).collect(Collectors.toList());
            this.batchDeleteClassAndSubInfo(user, courseId, preDeletedIdList);
        }

        List<Long> ids = new ArrayList<>();
        classList.forEach(c -> {
            if (c.getId() != null) {
                updateById(c);
            } else {
                Long id = insert(classConvert.convert(c));
                c.setId(id);
                ids.add(id);
            }
        });

        return ids;
    }

    /**
     * 批量删除class以及下面的小组、小组成员、班级成员、互动课件、视频、资料等所有的东西
     *
     * @param classIdList 需要删除的classId集合
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchDeleteClassAndSubInfo(Account user, Long courseId, List<Long> classIdList) {
        if (CollectionUtils.isEmpty(classIdList)) {
            return;
        }
        //解散班级
        classIdList.forEach(classId -> {
            ClassResultDTO classDTO = classMapper.findByClassId(classId);
            List<ClassStudentResultDTO> list = classStudentService.findByClassId(classId);
            classMqService.disbandClassSendNew(classDTO, list);
            classMqService.disbandClass(classId);
        });
        cascadeDeleteByClassIds(user, classIdList);
    }

    @Override
    public ClassResultDTO findDetail(ClassQueryDTO query) {
        ClassResultDTO result = baseMapper.findDetail(query);
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(result), "加课码已失效");
        ClassTeacherResultDTO teacher = baseMapper.findTeacherInfoByClassId(result.getId());
        result.setTeacher(teacher);
        return result;
    }

    /**
     * 班级名称是否可编辑，编辑名称只可编辑一次
     *
     * @param id 班级id
     * @return true-可编辑，false-不可编辑
     */
    @Override
    public Boolean isNameEditable(Long id) {
        ClassEntity old = Optional.ofNullable(getById(id)).orElseThrow(() -> CommonErrors.BAD_REQUEST.asException("班级数据不存在"));
        return Objects.equals(old.getIsNameEditable(), YesNoEnum.YES.ordinal());
    }

    @Override
    public List<ClassResultDTO> findByCourseIdAndIsTeacher(Long courseId, Long classId, Long studentId, Integer isTeacher, Integer isArchive) {
        return classMapper.findByCourseIdAndIsTeacher(courseId, classId, studentId, isTeacher, isArchive);
    }

    @Override
    public List<ClassResultDTO> findByCourseIdAndRoles(Long courseId, Long classId, Long studentId, List<String> classRoles, Integer isArchive) {
        return classMapper.findByCourseIdAndRoles(courseId, classId, studentId, classRoles, isArchive);
    }

    @Override
    public ClassResultDTO resetCode(Long id) {
        Long userId = LoginHelper.getUserId();
        ClassEntity old = Optional.ofNullable(getById(id)).orElseThrow(() -> CommonErrors.BAD_REQUEST.asException("班级数据不存在"));
        ClassStudentResultDTO student = classStudentService.findByStudentId(old.getId(), userId);
        if (Objects.equals(ClassRoleEnum.STUDENT.getCode(), student.getClassRole()) || Objects.equals(ClassRoleEnum.REPRESENTATIVE.getCode(), student.getClassRole())) {
            CommonErrors.BAD_REQUEST.check(false, "您暂无权限操作此功能");
        }
        classCode(old);
        updateById(old);
        return this.findClassInfoByClassId(id);
    }

    @Override
    public ClassResultDTO closeCode(Long id) {
        Long userId = LoginHelper.getUserId();
        ClassEntity old = Optional.ofNullable(getById(id)).orElseThrow(() -> CommonErrors.BAD_REQUEST.asException("班级数据不存在"));
        ClassStudentResultDTO student = classStudentService.findByStudentId(old.getId(), userId);
        if (Objects.equals(ClassRoleEnum.STUDENT.getCode(), student.getClassRole()) || Objects.equals(ClassRoleEnum.REPRESENTATIVE.getCode(), student.getClassRole())) {
            CommonErrors.BAD_REQUEST.check(false, "您暂无权限操作此功能");
        }
        old.setStatus(ClassStatusEnum.DISABLE);
        updateById(old);
        return this.findClassInfoByClassId(id);
    }

    @Override
    public ClassResultDTO openCode(Long id) {
        Long userId = LoginHelper.getUserId();
        ClassEntity old = Optional.ofNullable(getById(id)).orElseThrow(() -> CommonErrors.BAD_REQUEST.asException("班级数据不存在"));
        ClassStudentResultDTO student = classStudentService.findByStudentId(old.getId(), userId);
        if (Objects.equals(ClassRoleEnum.STUDENT.getCode(), student.getClassRole()) || Objects.equals(ClassRoleEnum.REPRESENTATIVE.getCode(), student.getClassRole())) {
            CommonErrors.BAD_REQUEST.check(false, "您暂无权限操作此功能");
        }
        old.setStatus(ClassStatusEnum.NORMAL);
        updateById(old);
        return this.findClassInfoByClassId(id);
    }

    @Override
    public ClassResultDTO findClassInfoByClassId(Long classId) {
        ClassQueryDTO query = new ClassQueryDTO();
        query.setId(classId);
        ClassResultDTO classResultDTO = this.findDetail(query);
        ClassTeacherResultDTO teacher = baseMapper.findTeacherInfoByClassId(classId);
        classResultDTO.setTeacher(teacher);
        Account account = LoginHelper.getXqLoginUser();
        classResultDTO.setUserInfo(account);
        ClassStudentResultDTO student = classStudentService.findByStudentId(classId, account.getId());
        if (Objects.nonNull(student)) {
            classResultDTO.setClassRole(student.getClassRole());

            setRelationAttribute(classResultDTO, student);
        }

        return classResultDTO;
    }

    @Override
    public ClassResultDTO findByCode(String code) {
        return baseMapper.findByCode(code);
    }

    /**
     * APP端返回
     *
     * @param courseCode 加课码
     * @return ClassAppResultDTO
     */
    @Override
    public ClassAppResultDTO findClassInfoByCode(String courseCode) {
        ClassAppResultDTO classAppResult = this.findClassInfoByCodeSimple(courseCode);
        //设置老师
        classAppResult.setTeacher(this.findTeacherByClassId(classAppResult.getId()));
        //设置审核状态和角色
        this.setAuditStatusAndRole(classAppResult);
        return classAppResult;
    }

    private void setRelationAttribute(ClassResultDTO classResultDTO, ClassStudentResultDTO classStudentResultDTO) {
        ClassRelationStudentResultDTO relationStudentResultDTO = new ClassRelationStudentResultDTO();
        relationStudentResultDTO.setId(classStudentResultDTO.getId());
        relationStudentResultDTO.setStudentName(classStudentResultDTO.getStudentName());
        relationStudentResultDTO.setRelationship(classStudentResultDTO.getRelationship());
        classResultDTO.setRelationStudent(relationStudentResultDTO);
    }

    private void setAuditStatusAndRole(ClassAppResultDTO classAppResultDTO) {
        ClassStudentEntity classStudentEntity = classStudentService.findByClassIdAndStudentId(classAppResultDTO.getId(), LoginHelper.getUserId(), YesNoEnum.NO.ordinal());
        //如果不在班级中
        if (Objects.isNull(classStudentEntity)) {
            //判断是否需要审核
            if (Objects.equals(ClassAuditEnum.AUTO.getCode(), classAppResultDTO.getAuditMethod())) {
                classAppResultDTO.setAuditStatus(ClassAuditStatusEnum.NOT_ACCEPT.getCode());
            } else {
                //设置登录人加课状态，老师直接显示已加入,查不到显示未申请
                ClassJoinAuditResultDTO classJoinAuditResult = classJoinAuditService.findLatestStatusByUserIdAndClassId(LoginHelper.getUserId(), classAppResultDTO.getId());
                //审核表中无数据
                if (Objects.isNull(classJoinAuditResult)) {
                    classAppResultDTO.setAuditStatus(ClassAuditStatusEnum.NOT_ACCEPT.getCode());
                    //审核表中有数据
                } else {
                    //如果是已通过，则说明是退出的班级
                    if (Objects.equals(ClassAuditStatusEnum.PASS.getCode(), classJoinAuditResult.getStatus())) {
                        classAppResultDTO.setAuditStatus(ClassAuditStatusEnum.NOT_ACCEPT.getCode());
                        //待审核和拒绝
                    } else {
                        classAppResultDTO.setAuditStatus(classJoinAuditResult.getStatus());
                    }
                }
            }

        } else {
            //在班级中，已通过
            classAppResultDTO.setAuditStatus(ClassAuditStatusEnum.PASS.getCode());
            classAppResultDTO.setClassRole(classStudentEntity.getClassRole().getCode());
        }
    }

    @Override
    public ClassTeacherResultDTO findTeacherByClassId(Long classId) {
        return baseMapper.findTeacherInfoByClassId(classId);
    }

    @Override
    public List<ClassResultDTO> findOtherClasByClassId(Long classId) {
        ClassResultDTO old = Optional.ofNullable(findById(classId)).orElseThrow(() -> CommonErrors.BAD_REQUEST.asException("班级数据不存在"));
        return this.findListByCourseIdAndNotEqCurrentClassIdAndClassRoleIsTeacher(old.getCourseId(), classId);
    }

    private List<ClassResultDTO> findListByCourseIdAndNotEqCurrentClassIdAndClassRoleIsTeacher(Long courseId, Long classId) {
        return this.baseMapper.findListByCourseIdAndNotEqCurrentClassIdAndClassRoleIsTeacher(courseId, classId, LoginHelper.getUserId(), YesNoEnum.YES.ordinal());
    }

    /**
     * 单个退课，只有关系用户才能退课
     *
     * @param id
     * @param smsCode
     * @param mobile
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean quitClass(Account user, Long id, String smsCode, String mobile) {
        String code = RedisUtils.getCacheObject(CacheConstants.CAPTCHA_CODE_KEY + mobile);
        CommonErrors.BAD_REQUEST.check(Objects.equals(smsCode, code), "验证码不正确");

        ClassResultDTO old = Optional.ofNullable(findById(id)).orElseThrow(() -> CommonErrors.BAD_REQUEST.asException("班级数据不存在"));
        ClassStudentResultDTO studentResultDTO = classStudentService.findAllRolesStudentByClassIdAndStudentId(id,
            LoginHelper.getUserId());
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(studentResultDTO), "该学生已退课");
        CommonErrors.BAD_REQUEST.check(Objects.equals(YesNoEnum.YES.ordinal(), old.getIsDropClass()), "该班级不允许退课");

        //删除学生
        classStudentService.deleteById(studentResultDTO.getId());

        //删除小组成员
        List<Long> studentList = new ArrayList<>();
        studentList.add(studentResultDTO.getStudentId());
        classGroupStudentService.deleteByClassIdAndStudentIdList(user, studentResultDTO.getClassId(), studentList);

        //删除审核记录
        ClassJoinAuditResultDTO classJoinAudit = classJoinAuditService.findByClassIdAndStudentId(studentResultDTO.getClassId(), studentResultDTO.getStudentId());
        if (Objects.nonNull(classJoinAudit)) {
            classJoinAuditService.deleteById(classJoinAudit.getId());
        }

        //退课记录
        classOperationLogService.insertStudentOperateLog(old.getCourseId(), old.getId(), studentResultDTO.getStudentId(), classJoinAudit.getId(), ClassOperationLogTypeEnum.QUIT);

        // 发送退课消息
        List<ClassStudentRemindDTO> studentRemindList = new ArrayList<>();
        ClassStudentRemindDTO studentRemind = new ClassStudentRemindDTO();
        studentRemind.setStudentId(studentResultDTO.getStudentId());
        studentRemind.setStudentName(studentResultDTO.getStudentName() + studentResultDTO.getRelationship());
        studentRemindList.add(studentRemind);
        classMessageSyncService.batchSyncSendMessage(old.getId(), studentRemindList, LoginHelper.getXqLoginUser());
        return true;
    }

    /**
     * 老师批量移出
     *
     * @param classId
     * @param classStudentIds 班级学生表的id
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer batchQuitClass(Account user, Long classId, List<Long> classStudentIds) {
        ClassResultDTO old = Optional.ofNullable(findById(classId)).orElseThrow(() -> CommonErrors.BAD_REQUEST.asException("班级数据不存在"));

        List<ClassStudentEntity> studentEntityList = classStudentService.findListByIds(classStudentIds);
        CommonErrors.BAD_REQUEST.check(CollectionUtils.isNotEmpty(studentEntityList), "学生数据不存在");
        List<Long> studentIdList = studentEntityList.stream().map(ClassStudentEntity::getStudentId).collect(Collectors.toList());

        //删除学生列表
        Integer result = classStudentService.deleteBatch(classStudentIds);

        //删除小组成员列表
        classGroupStudentService.deleteByClassIdAndStudentIdList(user, classId, studentIdList);

        //删除审核记录
        classJoinAuditService.deleteByClassIdAndStudentIdList(classId, studentIdList);

        //退课记录
        classOperationLogService.batchInsertStudentOperateLog(old.getCourseId(), old.getId(), studentIdList, null, ClassOperationLogTypeEnum.QUIT);

        // 退课提醒
        List<ClassStudentRemindDTO> studentRemindList = new ArrayList<>();
        studentEntityList.forEach(item -> {
            ClassStudentRemindDTO remindDTO = new ClassStudentRemindDTO();
            remindDTO.setStudentId(item.getStudentId());
            remindDTO.setStudentName(item.getStudentName() + item.getRelationship());
            studentRemindList.add(remindDTO);
        });
        classMessageSyncService.batchSyncSendMessage(old.getId(), studentRemindList, LoginHelper.getXqLoginUser());
        return result;
    }

    /**
     * 批量删除课程数据
     *
     * @param courseId
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteByCourseId(Account user, Long courseId) {
        List<ClassResultDTO> classResultList = Optional.ofNullable(findByCourseId(courseId)).orElseThrow(() -> CommonErrors.BAD_REQUEST.asException("班级数据不存在"));
        if (CollectionUtils.isNotEmpty(classResultList)) {
            batchDeleteClassAndSubInfo(user, courseId, classResultList.stream().map(ClassResultDTO::getId).collect(Collectors.toList()));
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cascadeDeleteByClassIds(Account user, List<Long> classIds) {
        //删除小组学生
        List<ClassGroupStudentEntity> classGroupStudentList = classGroupStudentService.findByClassIdList(classIds);
        if (CollectionUtils.isNotEmpty(classGroupStudentList)) {
            classGroupStudentService.batchDelete(user, classGroupStudentList.stream().map(classGroupStudentConvert::delete).collect(Collectors.toList()));
        }

        //删除小组
        List<ClassGroupEntity> classGroupList = classGroupService.findByClassIdList(classIds);
        if (CollectionUtils.isNotEmpty(classGroupList)) {
            classGroupService.removeBatchByIds(classGroupList.stream().map(ClassGroupEntity::getId).collect(Collectors.toList()));
        }

        //删除班级学生
        List<ClassStudentEntity> studentEntityList = classStudentService.findByClassIdList(classIds);
        if (CollectionUtils.isNotEmpty(studentEntityList)) {
            classStudentService.deleteBatch(studentEntityList.stream().map(ClassStudentEntity::getId).collect(Collectors.toList()));
        }

        //删除班级
        removeBatchByIds(classIds);
    }

    /**
     * 用户加入的班级数量，不计算老师和助教，不包括已经删除的班级
     *
     * @param userId
     * @return
     */
    @Override
    public Integer findAddedClassCountByUserId(Long userId) {
        return baseMapper.findAddedClassCountByUserId(userId);
    }

    @Override
    public List<ClassEntity> findByIdList(List<Long> classIdList) {
        return baseMapper.selectBatchIds(classIdList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void transferCourseClassList(Long newCourseId, Long userId, List<ClassEntity> classList) {
        classList.forEach(c -> {
            c.setCourseId(newCourseId);
            c.setUserId(userId);
        });
        saveOrUpdateBatch(classList);
    }

    @Override
    public ClassResultDTO findByClassId(Long classId) {
        return this.baseMapper.findByClassId(classId);
    }

    /**
     * 根据用户ID班级ID获取课程 班级 学生信息
     */
    @Override
    public ClassAllResultDTO classAllMessage(Long classId) {
        Long studentId = LoginHelper.getUserId();

        // 校验班级是否存在
        ClassResultDTO resultDTO = Optional.ofNullable(findById(classId)).orElseThrow(() -> CommonErrors.BAD_REQUEST.asException("数据不存在"));

        ClassAllResultDTO classAll = new ClassAllResultDTO();

        classAll.setClassDto(resultDTO);

        // 获取课程信息
        CourseResultDTO course = courseService.findById(resultDTO.getCourseId());
        classAll.setCourse(course);

        // 获取学生信息
        ClassStudentResultDTO student = classStudentService.findByStudentId(classId, studentId);
        classAll.setStudent(student);

        return classAll;
    }

    /**
     * 根据用户ID 用户角色 班级ID 课程内容ID 课程内容类型 获取课程 班级 学生信息
     */
    @Override
    public ClassAllResultDTO classAllMessage(Long classId, Integer messageType, Long contentId, String classRole) {

        // 校验班级是否存在
        ClassResultDTO resultDTO = Optional.ofNullable(findById(classId)).orElseThrow(() -> CommonErrors.BAD_REQUEST.asException("assistant.data.not.valid"));

        Long studentId = LoginHelper.getUserId();
        // 获取学生信息
        ClassStudentResultDTO student = classStudentService.findByStudentId(classId, studentId);
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(student), MessageUtils.message("assistant.data.not.valid"));

        // 判断老师角色是否一致
        if (Objects.equals(ClassRoleEnum.TEACHER.getCode(), student.getClassRole())) {
            if (!Objects.equals(ClassRoleEnum.TEACHER.getCode(), classRole)) {
                CommonErrors.BAD_REQUEST.check(false, MessageUtils.message("assistant.data.not.valid"));
            }
        }
        // 判断助教角色是否一致
        if (Objects.equals(ClassRoleEnum.ASSISTANT.getCode(), student.getClassRole())) {
            if (!Objects.equals(ClassRoleEnum.ASSISTANT.getCode(), classRole)) {
                CommonErrors.BAD_REQUEST.check(false, MessageUtils.message("assistant.data.not.valid"));
            }
        }
        // 判断课代表角色是否一致
        if (Objects.equals(student.getClassRole(), ClassRoleEnum.REPRESENTATIVE.getCode())) {
            if (!(Objects.equals(ClassRoleEnum.REPRESENTATIVE.getCode(), classRole) || Objects.equals(ClassRoleEnum.STUDENT.getCode(), classRole))) {
                CommonErrors.BAD_REQUEST.check(false, MessageUtils.message("assistant.data.not.valid"));
            }
        }
        // 判断学生角色是否一致
        if (Objects.equals(student.getClassRole(), ClassRoleEnum.STUDENT.getCode())) {
            if (!(Objects.equals(ClassRoleEnum.REPRESENTATIVE.getCode(), classRole) || Objects.equals(ClassRoleEnum.STUDENT.getCode(), classRole))) {
                CommonErrors.BAD_REQUEST.check(false, MessageUtils.message("assistant.data.not.valid"));
            }
        }

        // 获取课程信息
        CourseResultDTO course = courseService.findById(resultDTO.getCourseId());
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(course), MessageUtils.message("assistant.data.not.valid"));

        CommonErrors.BAD_REQUEST.check(courseContentService.checkCourseContent(messageType, contentId), MessageUtils.message("assistant.data.not.valid"));
        ClassAllResultDTO classAll = new ClassAllResultDTO();
        classAll.setStudent(student);
        classAll.setCourse(course);
        classAll.setClassDto(resultDTO);
        return classAll;
    }

    /**
     * 新奇提醒 -- 点击跳转时校验使用
     * 校验课程是否存在、班级是否存在、学生是否存在、选择校验课程内容是否存在
     * 返回课程信息、班级信息、学生信息
     *
     * @param :
     * @return :com.xinqi.modules.course.clazz.dto.rsp.ClassAllResultDTO
     * @Title:
     * @MethodName: classAllMessage
     * @Exception
     * @Description:
     * @author: yyzhen
     * @date: 2023/5/16 17:45
     */
    @Override
    public ClassAllResultDTO classAllMessage(RemindCheckDTO dto) {
        // 校验班级是否存在
        ClassResultDTO resultDTO = findById(dto.getClassId());
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(resultDTO), MessageUtils.message("data.does.not.exist"));
        // 获取课程信息
        CourseResultDTO course = courseService.findById(resultDTO.getCourseId());
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(course), MessageUtils.message("data.does.not.exist"));

        Long studentId = LoginHelper.getUserId();
        // 获取学生信息
        ClassStudentResultDTO student = classStudentService.findByStudentId(dto.getClassId(), studentId);
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(student), MessageUtils.message("data.does.not.exist"));
        // 学生归档状态-已归档
        CommonErrors.BAD_REQUEST.check(!Objects.equals(YesNoEnum.YES.ordinal(), student.getIsArchive()), MessageUtils.message("data.does.not.exist"));

        // 课程内容校验
        if (Objects.nonNull(dto.getContentType()) && Objects.nonNull(dto.getContentId())) {
            CommonErrors.BAD_REQUEST.check(courseContentService.checkCourseContent(dto.getContentType(), dto.getContentId()), MessageUtils.message("data.does.not.exist"));
        }

        ClassAllResultDTO classAll = new ClassAllResultDTO();
        classAll.setStudent(student);
        classAll.setCourse(course);
        classAll.setClassDto(resultDTO);
        return classAll;
    }

    @Override
    public List<ClassResultDTO> findArchivedClassAndRoleByCourseIdAndStudentId(Long courseId, Long userId, Integer isArchive) {
        return baseMapper.findArchivedClassAndRoleByCourseIdAndStudentId(courseId, userId, isArchive);
    }


    /**
     * 同步示范课程
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long insert(ClassEntity entity) {
        // 清空原有ID
        entity.setId(null);
        // 生成加课码
        classCode(entity);
        // 设置 拥有者
        entity.setCreateBy(entity.getUserId());
        // 保存 课程
        super.save(entity);
        // 同步课程菜单
        classMenuService.initClassMenu(entity.getId(), entity.getCourseId());
        return entity.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateBatch(List<ClassEntity> entityList) {
        return this.updateBatchById(entityList);
    }

    @Override
    public ClassAppResultDTO findClassInfoByCodeSimple(String courseCode) {
        ClassAppResultDTO classAppResult = Optional.ofNullable(baseMapper.findClassInfoByCode(courseCode)).orElseThrow(() -> CommonErrors.BAD_REQUEST.asException("加课码已失效"));

        if (StringUtils.isNotEmpty(classAppResult.getAuditInfo())) {
            classAppResult.setAuditInfoList(JSON.parseList(classAppResult.getAuditInfo(), String.class));
        }
        return classAppResult;
    }

    @Override
    public ShareClassResultDTO shareClassInfo(Long classId) {
        UserResultDTO user = R.as(userClient.findById(LoginHelper.getUserId()));
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(user), MessageUtils.message("用户不存在"));

        ClassResultDTO classEntity = findByClassId(classId);
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(classEntity), MessageUtils.message("班级不存在"));

        ShareClassResultDTO shareClassResultDTO = new ShareClassResultDTO();
        shareClassResultDTO.setCourseCode(classEntity.getCourseCode());
        shareClassResultDTO.setUserId(user.getId());
        shareClassResultDTO.setAvatar(user.getAvatar());
        shareClassResultDTO.setNickname(user.getNickname());
        shareClassResultDTO.setXinqiNumber(user.getXinqiNumber());
        shareClassResultDTO.setTitle(user.getNickname() + "邀请您加入他的课程");
        shareClassResultDTO.setContent(classEntity.getCourseName() + "·" + classEntity.getName());

        String logo = R.as(sysConfigClient.getConfigKey(ConfigKeyConstants.SYS_DEFAULT_LOGO));
        shareClassResultDTO.setLogo(logo);
        shareClassResultDTO.setUrl(String.format("code=%s&nickName=%s&avatar=%s", classEntity.getCourseCode(), user.getNickname(), user.getAvatar()));

        return shareClassResultDTO;
    }

    @Override
    public ClassAuditContentResultDTO getAuditContentInfo(Long classId) throws IOException {
        ClassAuditContentResultDTO resultDTO = new ClassAuditContentResultDTO();
        ClassEntity classEntity = this.baseMapper.selectById(classId);
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(classEntity), MessageUtils.message("班级不存在"));

        ClassStudentResultDTO classStudentResultDTO = classStudentService.findByClassIdAndStudentId(classId, LoginHelper.getUserId());
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(classStudentResultDTO), MessageUtils.message("学生不存在"));

        // 校验班级是否有审核信息 有则解析
        if (Objects.nonNull(classEntity.getAuditInfo())) {
            List<String> allAuditContent = JSON.parseList(classEntity.getAuditInfo(), String.class);
            resultDTO.setAllAuditContent(allAuditContent);
        } else {
            resultDTO.setAllAuditContent(new ArrayList<>());
        }

        // 校验用户是否 输入审核信息
        if (Objects.nonNull(classStudentResultDTO.getAuditContent())) {
            Map<String, String> completedAuditContent = JSON.toMapStr(classStudentResultDTO.getAuditContent());
            resultDTO.setCompletedAuditContent(completedAuditContent);
        } else {
            Map<String, String> emptyMap = new HashMap<>();
            resultDTO.setCompletedAuditContent(emptyMap);
        }


        resultDTO.setPerfect(checkAuditInfo(resultDTO.getAllAuditContent(), resultDTO.getCompletedAuditContent()));

        return resultDTO;
    }

    /**
     * 校验输入信息是否完善
     */
    private Boolean checkAuditInfo(List<String> allAuditContent, Map<String, String> completedAuditContent) {
        Boolean isPerfect = true;

        // 加入班级 审核信息为空 不需要完善信息
        if (CollectionUtils.isEmpty(allAuditContent)) {
            return false;
        }

        // 学生 输入审核信息为空 需要完善信息
        if (Objects.isNull(completedAuditContent) || completedAuditContent.isEmpty()) {
            return true;
        }

        for (String str : allAuditContent) {
            if (!completedAuditContent.containsKey(str)) {
                isPerfect = false;
            }
        }
        return isPerfect;
    }


}
